package obecto.core
{
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.text.TextField;
	
	import obecto.event.FLEvent;
	import obecto.layout.constraint.IConstraintLayoutElement;
	import obecto.metadata.MetadataDirectives;
	import obecto.metadata.PartDirective;
	import obecto.utility.MetaUtility;
	import obecto.utility.PartUtility;
	
	[Event(name="creationComplete", type="obecto.event.FLEvent")]
	[Event(name="sizeChanged", type="flash.events.Event")]
	[Event(name="viewChanged", type="flash.events.Event")]
	
	public class FLUIComponent extends Sprite implements IConstraintLayoutElement
	{
		public var asset : Object;
		
		/**
		 * Optimize for GPU rendering
		 */
		 
 		protected var _cacheInGPU : Boolean;
		[Bindable]
		public function set cacheInGPU(value : Boolean) : void
		{
			if (value == _cacheInGPU) return;
			
			_cacheInGPU = value;
			
			cacheAsBitmapMatrix = _cacheInGPU ? new Matrix() : null;
            cacheAsBitmap = _cacheInGPU;
		}
		public function get cacheInGPU() : Boolean
		{
			return _cacheInGPU;
		}
		
		/**
		 * Overriden for the purpose of adding data binding capabilities.
		 * If you want to control the positioning and the sizing in some special and custom way,
		 * you need to override this method.
		 */
		 
		[Bindable]
		override public function set x(value : Number) : void
		{
			super.x = value;
		}
		override public function get x() : Number
		{
			return super.x;
		}
		
		/**
		 * Overriden for the purpose of adding data binding capabilities.
		 * If you want to control the positioning and the sizing in some special and custom way,
		 * you need to override this method.
		 */

		[Bindable]
		override public function set y(value : Number) : void
		{
			super.y = value;
		}
		override public function get y() : Number
		{
			return super.y;
		}
		
		protected var preinitializeWidth : Number = 0;
		protected var preinitializeWidthChanged : Boolean = false;
		[Bindable(event="sizeChanged")]
		/**
		 * Overriden for the purpose of adding data binding capabilities.
		 * If you want to control the positioning and the sizing in some special and custom way,
		 * you need to override this method.
		 */ 
		override public function set width(value : Number) : void
		{
			if (initialized)
			{
				super.width = value;
			}
			else
			{
				preinitializeWidth = value;
				preinitializeWidthChanged = true;
			}

			dispatchEvent(new Event("sizeChanged"));
		}
		override public function get width() : Number
		{
			if (initialized || !preinitializeWidthChanged)
			{
				return super.width;
			}
			else
			{
				return preinitializeWidth;
			}
		}
		
		protected var preinitializeHeight : Number = 0;
		protected var preinitializeHeightChanged : Boolean = false;
		[Bindable(event="sizeChanged")]
		/**
		 * Overriden for the purpose of adding data binding capabilities.
		 * If you want to control the positioning and the sizing in some special and custom way,
		 * you need to override this method.
		 */ 
		override public function set height(value : Number) : void
		{
			if(initialized)
			{
				super.height = value;
			}
			else
			{
				preinitializeHeight = value;
				preinitializeHeightChanged = true;
			}
			
			dispatchEvent(new Event("sizeChanged"));
		}
		override public function get height() : Number
		{
			if(initialized || !preinitializeHeightChanged)
			{
				return super.height;
			}
			else
			{
				return preinitializeHeight;
			}
		}
		
		[Bindable] public var initialized : Boolean = false;
		
		protected var _constraints : Array;
		[Bindable]
		public function set constraints(value : Array) : void
		{
			if(_constraints != value)
			{
				_constraints = value;
			}
		}
		public function get constraints() : Array
		{
			return _constraints;
		}
		
		[Bindable]
		protected var _view : DisplayObject;
		
		[Bindable(event="viewChanged")]
		public function set view(value : DisplayObject) : void
		{
			_view = value;
			initializeParts();
			
			dispatchEvent(new Event("viewChanged"));
		} 
		public function get view() : DisplayObject
		{
			return _view;
		} 
		
		
		protected var metadata : MetadataDirectives;
		
		protected var partsInitialized : Boolean = false;
		
		public function FLUIComponent()
		{
			super();
			
			FLGlobals.topLevelSprite.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
		}
		
		/**
		 * In most of the cases you won't need to override initializeParts, 
		 * but for components that need to add manually view parts or draw them 
		 * in run-time, this is the right method for the job.
		 * 
		 * It's up to you whether to call super.initializeParts()
		 */ 
		protected function initializeParts() : void
		{
			metadata = MetaUtility.resolveMetadata(this);
			
			if (_view == null && asset != null)
			{
				var assetClass : Class = Class(asset);
				_view = DisplayObject(new assetClass());
			}
			
			if (_view != null)
			{
				if (_view.parent == null)
				{
					addChild(_view);
				}
				
				for each (var directive : PartDirective in metadata.parts)
				{
					var part:DisplayObject = PartUtility.initializePart(this, _view, directive);
                    if (part is TextField)
                    {
                        TextField(part).embedFonts = true;
                    }
				}
				
				partsInitialized = true;
				
				dispatchEvent(new Event("viewChanged"));
			}
			else
			{
				partsInitialized = true;
				for each (directive in metadata.parts)
				{
					PartUtility.removePart(this, _view, directive);
					partsInitialized = false;
				}
			}			
		}
		
		protected function initializeDimensions() : void 
		{
			if (preinitializeWidthChanged) { super.width = preinitializeWidth; }
			if (preinitializeHeightChanged) { super.height = preinitializeHeight; }
			
			dispatchEvent(new Event("sizeChanged"));
		}
		
		/**
		 * initialize is an abstract method that needs to be implemented in subclasses:
		 */ 
		virtual protected function initialize() : void 
		{
			return;
		}
		
		virtual protected function handleEnterFrame(e : Event) : void
		{
			FLGlobals.topLevelSprite.removeEventListener(Event.ENTER_FRAME, handleEnterFrame);
			
			initializeParts();
			initializeDimensions(); // set the preinitialize width and height
			initialize(); // perform custom initialization:
			
			initialized = true;
			
			dispatchEvent(new FLEvent(FLEvent.CREATION_COMPLETE));
		}
		
		//For test purposes
		public function get superWidth() : Number
		{
			return super.width;
		}
		
		//For test purposes
		public function get superHeight() : Number
		{
			return super.height;
		}
	}
}